package base.net.socket
{
	import utils.hurlant.crypto.rsa.RSAKey;
	import utils.hurlant.math.BigInteger;
	
	import flash.events.Event;
	import flash.events.EventDispatcher;
	import flash.events.IOErrorEvent;
	import flash.events.ProgressEvent;
	import flash.events.SecurityErrorEvent;
	import flash.net.Socket;
	import flash.utils.ByteArray;
	
	import base.io.ByteBuffer;
	import base.net.socket.events.SocketConnectionEvent;
	import base.net.socket.packet.SocketReceivablePacket;
	import base.net.socket.packet.SocketSendablePacket;
	import base.types.SocketDataStructureType;
	
	import utils.ByteUtil;
	import utils.CryptoUtil;
	import utils.RC4Util;
	
	public class SocketConnection extends EventDispatcher
	{
		/**
		 *Socket 
		 */		
		private var _socket:Socket;
		/**
		 *主机地址 
		 */		
		private var _host:String;
		/**
		 *主机端口 
		 */		
		private var _port:int;
		/**
		 *标识ID，全局唯一值 
		 */
		private var _id:int;
		/**
		 * 标识ID，全局唯一值
		 */
		public function get id():int
		{
			return _id;
		}
		/**
		 *SocketConnection连接校验码 
		 */		
		private var _connectionCheckCode:int;
		/**
		 * SocketConnection连接校验码
		 */
		public function get connectionCheckCode():int
		{
			return _connectionCheckCode;
		}
		/**
		 * 当前TCP发送最大数据包序号，此序号与发送方需保持一致
		 */
		private var _sendPacketNumberMax:int=0;
		/**
		 *通讯数据包加密密码 
		 */		
		private var _cryptoKeys:Array;
		/**
		 * 通讯数据包加密密码
		 */
		public function get cryptoKeys():Array
		{
			return _cryptoKeys;
		}
		
		/**
		 *TCP连接是否握手完成 
		 */		
		private var _handshakeCompleteTcp:Boolean=false;
		/**
		 * TCP连接是否握手完成
		 */
		public function get handshakeCompleteTcp():Boolean
		{
			return _handshakeCompleteTcp;
		}
		
		/**
		 * 是否已读取过包种类标识
		 */
		private var _isReadPacketType:Boolean=false;
		/**
		 * 是否已读取过包头
		 */
		private var _isReadHead:Boolean=false;
		/**
		 * 包头长度
		 */
		private var _packetHeadLength:int=0;
		/**
		 * 包体长度
		 */
		private var _packetBodyLength:int=0;
		/**
		 * 包尾长度
		 */
		private var _packetEndLength:int=0;
		/**
		 * 包种类标识
		 */
		private var _socketDataStructureType:int;
		
		/**
		 * Socket连接类 
		 * @param host 主机地址
		 * @param port 主机端口
		 * 
		 */		
		public function SocketConnection(host:String, port:int)
		{
			_host=host;
			_port=port;
		}
		
		/**
		 *执行Socket连接 
		 * @param tcp 是否执行tcp连接
		 * 
		 */				
		public function connect():void
		{
			_socket = new Socket();
			_socket.timeout=SocketConfig.TCP_CONNECT_TIMEOUT;
			_socket.addEventListener(Event.CONNECT, handler);
			_socket.addEventListener(Event.CLOSE, handler);
			_socket.addEventListener(IOErrorEvent.IO_ERROR, handler);
			_socket.addEventListener(SecurityErrorEvent.SECURITY_ERROR, handler);
			_socket.addEventListener(ProgressEvent.SOCKET_DATA, handler);
			_socket.connect(_host, _port);
		}
		
		/**
		 * 发送 
		 * @param byteArray 要发送的源数据
		 */		
		private function send(byteArray:ByteArray):void
		{
			_socket.writeBytes(byteArray);//数据包内容
			_socket.flush();
			byteArray.clear();
			byteArray=null;
		}
		
		/**
		 *连接事件
		 */		
		private function handler(event:Event):void
		{
			switch(event.type)
			{
				case Event.CLOSE:
					close();
					dispatchEvent(new SocketConnectionEvent(SocketConnectionEvent.CLOSE));
					break;
				case Event.CONNECT:
					dispatchEvent(new SocketConnectionEvent(SocketConnectionEvent.CONNECTABLE));
					break;
				case IOErrorEvent.IO_ERROR:
					dispatchEvent(new SocketConnectionEvent(SocketConnectionEvent.IO_ERROR));
					break;
				case SecurityErrorEvent.SECURITY_ERROR:  
					dispatchEvent(new SocketConnectionEvent(SocketConnectionEvent.SECURITY_ERROR));  
					break;
				case ProgressEvent.SOCKET_DATA:
					receivable();
					break;  
			}  
		}
		
//		/**
//		 * 当前TCP接收最大数据包序号，此序号与发送方需保持一致
//		 */
//		private var _receivePacketNumberMax:int=0;
		/**
		 * 当前数据包序号
		 */
		private var _receivePacketNumber:int=0;
		
		/**
		 * 处理数据
		 */
		private function receivable():void
		{
			if(!_isReadPacketType && _socket.bytesAvailable>=SocketConfig.PACKET_STRUCTURE_TYPE_LENGTH)
			{//取得包种类标识
				_socketDataStructureType=_socket.readByte();//取得包种类标识
				switch(_socketDataStructureType)
				{
					case SocketDataStructureType.HANDSHAKE_TCP://控制包-TCP协议握手
						_isReadPacketType=true;
						_packetHeadLength=SocketConfig.PACKET_BODY_LENGTH;
						_packetEndLength=0;
						break;
					case SocketDataStructureType.HANDSHAKE_TCP_ACK_ACK://控制包-TCP协议握手应答的应答
						_isReadPacketType=false;
						dispatchEvent(new SocketConnectionEvent(SocketConnectionEvent.HANDSHAKE_COMPLETE));
						receivable();//直接读取下一个包
						break;
					case SocketDataStructureType.DATA://数据包
						_isReadPacketType=true;
						_packetHeadLength=SocketConfig.PACKET_NUMBER_LENGTH+SocketConfig.PACKET_BODY_LENGTH;
						_packetEndLength=SocketConfig.PACKET_END_LENGTH;
						break;
				}
			}
			
			if (_isReadPacketType && !_isReadHead && _socket.bytesAvailable>=_packetHeadLength)
			{//取得包头
				switch(_socketDataStructureType)
				{
					case SocketDataStructureType.DATA:
						_receivePacketNumber=_socket.readInt();
						break;
				}
				_packetBodyLength=_socket.readShort();//取得包体数据长度(不包括包头，包尾)
				_isReadHead=true;
			}
			
			if(_isReadHead && _socket.bytesAvailable>=_packetBodyLength+_packetEndLength)
			{//已经取得了包头，并且当前装载缓冲区的数据满足包体加包尾的长度，则进行完整包数据填充
				var bodyByteBuffer:ByteBuffer =new ByteBuffer();
				_socket.readBytes(bodyByteBuffer, 0, _packetBodyLength);//取得包体数据
				var endCode:ByteArray=new ByteArray();
				if(_packetEndLength>0 && _socket.bytesAvailable>=_packetEndLength)
				{
					_socket.readBytes(endCode, 0, _packetEndLength);//取得包尾校验串
				}
				
				var baStr:String="";
				for(var i:int=0;i<endCode.length;i++)
				{
					baStr+=", "+endCode[i];
				}
//				trace("收到服务器的加密endCode:"+baStr);
				
				switch(_socketDataStructureType)
				{
					case SocketDataStructureType.DATA:
//						if(_receivePacketNumber!=_receivePacketNumberMax)
//						{
//							trace("TCP包执行包序号校验未通过 receivePacketNumber:"+_receivePacketNumber+" receivePacketNumberMax:"+_receivePacketNumberMax);
//						}
//						else
//						{
//							_receivePacketNumberMax++;
							var encryptCode:ByteArray = CryptoUtil.decryptionPacket(_socketDataStructureType, _id, _connectionCheckCode, _receivePacketNumber, bodyByteBuffer, _cryptoKeys);
							if(ByteUtil.bytesCompare(endCode, encryptCode))
							{
								receiveByteBufferComplete(_socketDataStructureType, bodyByteBuffer);
							}
							else
							{
								trace("包尾校验码未匹配");
							}
//						}
						break;
					case SocketDataStructureType.HANDSHAKE_TCP://TCP协议握手(由TCP接收端(服务端)发送握手数据给TCP发送端(客户端))，说明当前的SocketConnectionBase为客户端连接，需要设置由服务器发过来的握手数据
						_id=bodyByteBuffer.readInt();//取得并设置当前SocketConnectionBase标识ID
						_connectionCheckCode=bodyByteBuffer.readInt();//取得并设置当前SocketConnectionBase连接校验码
						var modulusLen:int=bodyByteBuffer.readInt();
						var modulus:ByteArray = new ByteArray();
						bodyByteBuffer.readBytes(modulus, 0, modulusLen);//取得TCP协议握手RSA公钥modulus
						var publicExponentLen:int=bodyByteBuffer.readInt();
						var publicExponent:ByteArray = new ByteArray();
						bodyByteBuffer.readBytes(publicExponent, 0, publicExponentLen);//取得TCP协议握手RSA公钥publicExponent
						var modulusBigInteger:BigInteger=new BigInteger(modulus);
						var publicExponentBigInteger:BigInteger=new BigInteger(publicExponent);
						var publicKey:RSAKey=new RSAKey(modulusBigInteger, publicExponentBigInteger.intValue());
						var cryptoStr:String = CryptoUtil.genRandomString(64);//生成一个64字节长度的随机字符串，用做密码
						var cryptoByteArrar:ByteArray=ByteUtil.getBytesByString(cryptoStr);
						_cryptoKeys = RC4Util.getKey(cryptoByteArrar);//生成通讯数据加密密码bytes，用与发给服务器，以及客户端自已加密用;
						var rsaCryptoKeys:ByteArray=new ByteArray();
						publicKey.encrypt(cryptoByteArrar, rsaCryptoKeys, cryptoByteArrar.length);//将通讯密码使用公钥加密
						_handshakeCompleteTcp=true;
						tcpToTcpHandshakeAck(rsaCryptoKeys);
						break;
				}
				_isReadPacketType=false;
				_isReadHead=false;
				receivable();
			}
		}
		
		/**
		 * 接受数据完成，最后操作
		 * @param socketType SocketType
		 * @param packByteBuffer 数据体
		 */
		private function receiveByteBufferComplete(socketDataStructureType:int, packByteBuffer:ByteBuffer):void
		{
			var receivablePacket:SocketReceivablePacket=new SocketReceivablePacket(packByteBuffer.readShort(), socketDataStructureType);//创建接收到的数据包
			receivablePacket.writeBytes(packByteBuffer, packByteBuffer.position);
			receivablePacket.flip();
			dispatchEvent(new SocketConnectionEvent(SocketConnectionEvent.RECEIVABLE_PACKET, receivablePacket));
		}
		
		/**
		 * 发送端(客户端)TCP向接收端(服务端)TCP发送协议握手成功应答数据
		 * @param cryptoKeys 通讯加密密码
		 */
		public function tcpToTcpHandshakeAck(cryptoKeys:ByteArray):void
		{
			var socketSendablePacket:SocketSendablePacket=new SocketSendablePacket(0, SocketDataStructureType.HANDSHAKE_TCP_ACK);
			socketSendablePacket.writeBytes(cryptoKeys);//通讯加密密码
			sendPacket(socketSendablePacket);
		}
		
		/**
		 * 发送数据包
		 * @param socketSendablePacket 数据包
		 */
		public function sendPacket(socketSendablePacket:SocketSendablePacket):void
		{
			switch(socketSendablePacket.socketDataStructureType)
			{
				case SocketDataStructureType.DATA:
					socketSendablePacket.setPacketNumber(_sendPacketNumberMax);
					var headLen:int=SocketConfig.PACKET_STRUCTURE_TYPE_LENGTH+SocketConfig.PACKET_NUMBER_LENGTH+SocketConfig.PACKET_BODY_LENGTH;//包头长度
					encryptPacket(headLen, _sendPacketNumberMax, socketSendablePacket);
					_sendPacketNumberMax++;
					break;
			}
			socketSendablePacket.end();
			send(socketSendablePacket);
			socketSendablePacket.dispose();
			socketSendablePacket=null;
		}
		
		private function encryptPacket(headLen:int, packetNumber:int, sendablePacket:SocketSendablePacket):void
		{
			var sendablePacketStr:String="";
			for(var i:int=0;i<sendablePacket.length;i++)
			{
				sendablePacketStr+=", "+sendablePacket[i];
			}
//			trace("加密前sendablePacket:"+sendablePacketStr);
			
			sendablePacket.flip();
			var bodyBytes:ByteArray=new ByteArray();
			sendablePacket.position=headLen;
			sendablePacket.readBytes(bodyBytes);
			
			var baStr:String="";
			for(i=0;i<bodyBytes.length;i++)
			{
				baStr+=", "+bodyBytes[i];
			}
//			trace("加密前读取的body:"+baStr);
			
			sendablePacket.position=headLen;
			var encryptCode:ByteArray = CryptoUtil.encryptPacket(sendablePacket.socketDataStructureType, _id, _connectionCheckCode, packetNumber, bodyBytes, cryptoKeys);
			sendablePacket.writeBytes(bodyBytes);//加入包体
			
			baStr="";
			for(i=0;i<bodyBytes.length;i++)
			{
				baStr+=", "+bodyBytes[i];
			}
//			trace("加密后读取的body:"+baStr);
			
			sendablePacket.writeBytes(encryptCode);//加入包尾校验串
			
//			trace("包尾校验串:"+encryptCode);
		}
		
		public function close():void
		{
			if(_socket)
			{
				_socket.close();
				_socket=null;
			}
			_cryptoKeys=null;
		}
	}
}